#include "llOption.hpp"
#include "llSystemInfo.hpp"
#include "llCommandList.hpp"
#include "llMakeDirectory.hpp"
#include "llReporter.hpp"
using namespace llmake;

#include <boost/filesystem.hpp>
#include <boost/algorithm/string.hpp>
using namespace boost::filesystem;

#include <sstream>

#include <boost/tr1/memory.hpp>
using namespace std::tr1;
extern shared_ptr<llSystemInfo> global_SystemInfo;
extern shared_ptr<llOption> global_Option;

void llCommandBase::dumpCommands(llCommandList &commands)
{
	llCommandList::iterator it = commands.begin();
	for( ; commands.end() != it ; it++ )
	{
		cout<<(*it)->m_Name<<"(";
		llTokenList::iterator itToken = (*it)->m_Tokens.begin();
		for( ; (*it)->m_Tokens.end() != itToken;itToken++)
		{
			cout<<" "<<itToken->m_Token;
		}
		cout<<")"<<endl;
	}
}

string llCommandBase::parseSource(void)
{
	string allObject("");

	llTokenList::iterator itr = m_Tokens.begin();
	for( ; m_Tokens.end() != itr ; itr++ )
	{
//		cout<<" <"<<itr->m_Token<<":"<<itr->m_Joint<<"> ";
		string sourcePath = m_Owner->getFolder() + "/" + itr->m_Token;
		sourcePath = global_SystemInfo->simplePath(sourcePath);
#ifdef DEBUG			
		cout<<"sourcePath ["<<sourcePath<<"]"<<endl;
#endif
		path full_path( sourcePath );
		string ext = full_path.extension();
		boost::to_lower(ext);
		string fileName = full_path.filename();
//		boost::to_lower(fileName);
#ifdef DEBUG			
		cout<<"sourcePath ["<<sourcePath<<"]"<<"ext ["<<ext<<"]"<<"fileName ["<<fileName<<"]"<<endl;
#endif
		string compilerString;
		llExecuteCommandNode command;
		string type; 
		if( ".h" == ext )
		{
			continue;
		}
		else if( ".hpp" == ext )
		{
			continue;
		}
		else if( ".c" == ext )
		{
			type = "cflag";
			command.m_Command = "${CC}";
		}
		else if( ".cpp" == ext || ".cp" == ext || ".cx" == ext || ".cxx" == ext)
		{
			type = "cxxflag";
			command.m_Command = "${CXX}";
		}
		else if( ".s" == ext || ".asm" == ext)
		{
			continue;
		}
		else
		{
			continue;
		}
		string object = global_SystemInfo->m_PWD + "/Bytecode/" + fileName+".bc";
		allObject += object + " ";
		compilerString += sourcePath;
		compilerString += " -o ";
		compilerString += object;
		compilerString += m_Owner->getFlags(type);
#ifdef DEBUG			
		cout<<"compilerString ["<<compilerString<<"]"<<endl;
#endif
		command.m_Body = compilerString;
		command.m_Src.push_back(sourcePath);
		command.m_Dst.push_back(object);
		m_Owner->insertExecuteCommand(command);
	}
	return allObject;
}

/****************************************************************************************************/
/****************************************************************************************************/
/****************************************************************************************************/
void llCommand__project::parseCommand(llCommandList &commands)
{
	llTokenList tokens;
	llTokenNode node;
	if( global_Option->m_CMake )
	{
		node.m_Token = global_SystemInfo->m_CMakeModulePath;
		tokens.push_back(node);
#ifdef DEBUG			
	cout<<"node.m_Token ["<<node.m_Token<<"]"<<endl;
#endif
		m_Owner->insertVar( "CMAKE_MODULE_PATH" , tokens );

		tokens.clear();
		node.m_Token = m_Owner->getFolder();
		tokens.push_back(node);
#ifdef DEBUG			
	cout<<"node.m_Token ["<<node.m_Token<<"]"<<endl;
#endif
		m_Owner->insertVar( "CMAKE_CURRENT_SOURCE_DIR" , tokens );
		
	}
	commands.pop_front();
}
/****************************************************************************************************/
/****************************************************************************************************/
/****************************************************************************************************/
void llPreProcess__selfDefine::preProcess(llCommandList &commandsSrc)
{
#ifdef DEBUG			
	cout<<"preProcess command ["<<m_Name<<"]"<<endl;
#endif
	commandsSrc.pop_front();
	llMacroAndFunctionNode macroNode;
	if ( true == m_Owner->findMacroAndFunction(m_Name,macroNode) )
	{
		cout<<" start preparse command ["<<m_Name<<"]"<<endl;

		llTokenList varRealArgv = this->m_Tokens;
		llTokenList varMacroArgv = macroNode.m_ARGV;
		llCommandList toBeReplaced = macroNode.m_Body;
		llVariableMap replaceMap;
		replaceMap["ARGV"] = varRealArgv;
		llTokenList::iterator itrRealAgrv = varRealArgv.begin();
		llTokenList::iterator itrMacroAgrv = varMacroArgv.begin();
		for(; varRealArgv.end() != itrRealAgrv && varMacroArgv.end()!= itrMacroAgrv; itrRealAgrv++, itrMacroAgrv++ )
		{
			llTokenList tmplist;
			tmplist.push_back(*itrRealAgrv);
			replaceMap[itrMacroAgrv->m_Token] = tmplist;
		}
		llTokenList argnlist;
		for(; varRealArgv.end() != itrRealAgrv ; itrRealAgrv++ )
		{
			argnlist.push_back(*itrRealAgrv);
		}
		replaceMap["ARGN"] = argnlist;
		llTokenNode argcNode;
		stringstream argcSS;
		argcSS<<varRealArgv.size();
		argcNode.m_Token = argcSS.str();
		llTokenList argclist;
		argclist.push_back(argcNode);
		replaceMap["ARGC"] = argclist;
		llCommandList newList;
		m_Owner->replaceCommandLists(toBeReplaced,newList,replaceMap);
		llCommandList::iterator iter = newList.begin();
		for( ; newList.end() != iter ; iter++ )
		{
			commandsSrc.push_front(*iter);
		}
	}
	else
	{
		cout<<" Can not find command ["<<m_Name<<"] handler"<<endl;
	}
}
/****************************************************************************************************/
/****************************************************************************************************/
/****************************************************************************************************/
bool llPreProcess__include::checkFileExists(string &name)
{
	string root = m_Owner->getFolder();
	string fileName = root + "/"+ m_Tokens.front().m_Token;
	path full_path( fileName );
	if(exists(full_path))
	{
		name = fileName;
		return true;
	}
	else
	{
		if( global_Option->m_CMake )
		{
			llTokenList tokens;
			m_Owner->getVar( "CMAKE_MODULE_PATH" , tokens );
			llTokenList::iterator it = tokens.begin();
			for( ; tokens.end() != it ; it++ )
			{
				fileName = it->m_Token + "/"+ m_Tokens.front().m_Token + ".cmake";
#ifdef DEBUG			
	cout<<"try find fileName ["<<fileName<<"]"<<endl;
#endif
				path full_path2( fileName );
				if(exists(full_path2))
				{
#ifdef DEBUG			
	cout<<"catch fileName ["<<fileName<<"]"<<endl;
#endif
					name = fileName;
					return true;
				}
			}
		}
	}
	name = "";
	return false;
}

void llPreProcess__include::preProcess(llCommandList &commandsSrc)
{
#ifdef DEBUG			
	cout<<"preProcess command ["<<m_Name<<"]"<<endl;
#endif
//	string root = m_Owner->getFolder();
//	string fileName = root + "/"+ m_Tokens.front().m_Token;
#ifdef DEBUG			
	cout<<"include file name "<<fileName<<endl;
#endif
//	path full_path( fileName );
	llCommandList commandsInclude;
	string fileName;
	if( checkFileExists(fileName) )
//	if(exists(full_path))
	{
		llTokenEngine tokenEngine(fileName);
		llTokenList tokens;
		tokenEngine.getToken(tokens);
		llLexEngine lexEngine(tokens);
		llLexList lexs;
		lexEngine.getLex(lexs);
		llSyntaxEngine syntaxEngine(lexs);
		syntaxEngine.setOwner(m_Owner);
		syntaxEngine.getCommand(commandsInclude);
	}
	else
	{
		cout<<"Can not find file "<<m_Tokens.front().m_Token<<endl;		
	}
	commandsSrc.pop_front();
	llCommandList::reverse_iterator iter = commandsInclude.rbegin();
	for( ; commandsInclude.rend() != iter ; iter++ )
	{
		commandsSrc.push_front(*iter);
	}
#ifdef DEBUG
	cout<<"------------------------------------------------------------------------"<<endl;
	dumpCommands(commandsSrc);
	cout<<"*************************************************************************"<<endl;
#endif

}
/****************************************************************************************************/
/****************************************************************************************************/
/****************************************************************************************************/
void llPreProcess__add_subdirectory::preProcess(llCommandList &commandsSrc)
{
	commandsSrc.pop_front();
#ifdef DEBUG			
	cout<<"preProcess command ["<<m_Name<<"]"<<endl;
#endif
	string root = m_Owner->getFolder();
	string subPath = root + "/"+ m_Tokens.front().m_Token;
#ifdef DEBUG			
	cout<<"sub path name "<<subPath<<endl;
#endif
	shared_ptr<llMakeDirectory> subDir(new llMakeDirectory(subPath));
	subDir->setTop(m_Owner);
//	m_Owner->insertSubDir(subDir);
	subDir->doParse();
}
/****************************************************************************************************/
/****************************************************************************************************/
/****************************************************************************************************/
void llPreProcess__set::preProcess(llCommandList &commandsSrc)
{
#ifdef DEBUG			
	cout<<"preProcess command ["<<m_Name<<" ]"<<m_Tokens.front().m_Token<<endl;
#endif
	llTokenNode name = m_Tokens.front();
	m_Tokens.pop_front();
	m_Owner->insertVar(name.m_Token,m_Tokens);
	commandsSrc.pop_front();
}
/****************************************************************************************************/
/****************************************************************************************************/
/****************************************************************************************************/
void llPreProcess__macro::preProcess(llCommandList &commandsSrc)
{
#ifdef DEBUG			
	cout<<"preProcess command ["<<m_Name<<"]"<<m_Tokens.front().m_Token<<endl;
#endif
	llMacroAndFunctionNode macroNode;
	macroNode.m_Name = m_Tokens.front();
	m_Tokens.pop_front();
	macroNode.m_ARGV = m_Tokens;
	commandsSrc.pop_front();
	bool endFlag = false;
	llCommandList::iterator iter;
	do {
		iter = commandsSrc.begin();
		if( "endmacro" == (*iter)->m_Name)
		{
			if ( macroNode.m_Name.m_Token == ((*iter)->m_Tokens.front()).m_Token )
			{
				endFlag = true;
				break;
			}
		}
		macroNode.m_Body.push_back(*iter);
		commandsSrc.pop_front();
	} while(commandsSrc.end() != iter);
	
	m_Owner->insertMacroAndFunction(macroNode.m_Name.m_Token,macroNode);
}
/****************************************************************************************************/
/****************************************************************************************************/
/****************************************************************************************************/
bool llPreProcess__if::calcExpr( llTokenList &expr,string & result)
{
	result = "";
	llTokenList::iterator exprIt = expr.begin();
	for( ; expr.end() != exprIt ; exprIt++ )
	{
		result += exprIt->m_Token;
	}
/*
now only do simple things
*/
	if( 1 == expr.size() )
	{
#ifdef DEBUG
		cout<<"m_Token "<<expr.front().m_Token<<endl;
#endif
		if( global_SystemInfo->getSystemExpr(expr.front().m_Token))
		{
			result += "=true";
			return true;
		}
	}
	result += "=false";
	return false;
}
string llPreProcess__if::calcExpr( list<llTokenList> &exprList)
{
	string ret = "";
	string resultString;
	string elseString;
	if( this->calcExpr( exprList.front() ,resultString ))
	{
		return resultString;
	}
	else
	{
		exprList.pop_front();
		list<llTokenList>::iterator it = exprList.begin();
		for( ; exprList.end() != it ; it++ )
		{
//			elseString = "";
			if( this->calcExpr( *it ,elseString ))
			{
				break;
			}
		}
	}
	return resultString + elseString ;
}
void llPreProcess__if::calcBlock(llCommandList &commandsSrc,map<string,llCommandList> &block,list<llTokenList> &exprList)
{
	llTokenList expr = m_Tokens;
	string exprString= "";
	llTokenList::iterator exprIt = expr.begin();
	for( ; expr.end() != exprIt ; exprIt++ )
	{
		exprString += exprIt->m_Token;
	}
	exprList.push_back(expr);
	commandsSrc.pop_front();
	string key = exprString + "=true";
	llCommandList tmpList;
	while( false == commandsSrc.empty() )
	{
		llCommandBasePtr cur = commandsSrc.front();
		commandsSrc.pop_front();
		if( "else" == cur->m_Name && ( expr == cur->m_Tokens || cur->m_Tokens.empty() ) )
		{
			block[key] = tmpList;
			key = exprString + "=false";
			tmpList.clear();
		}
		else if( "endif" == cur->m_Name && ( expr == cur->m_Tokens || cur->m_Tokens.empty() ) )
		{
			break;
		}
		else if( "elseif" == cur->m_Name )
		{
			block[key] = tmpList;
			exprList.push_back(cur->m_Tokens);

			string elseExprStr= "";
			llTokenList::iterator elseIt = cur->m_Tokens.begin();
			for( ; cur->m_Tokens.end() != elseIt ; elseIt++ )
			{
				elseExprStr += elseIt->m_Token;
			}
			key = exprString + "=false" + elseExprStr+"=true";
			tmpList.clear();
		}
		else
		{
			tmpList.push_back(cur);
		}
	}
	block[key] = tmpList;
}
void llPreProcess__if::preProcess(llCommandList &commandsSrc)
{
#ifdef DEBUG			
	cout<<"preProcess command ["<<m_Name<<"]"<<m_Tokens.front().m_Token<<endl;
#endif
	map<string,llCommandList> block;
	list<llTokenList> exprList;
	calcBlock(commandsSrc,block,exprList);
	string blockKey = calcExpr(exprList);
#ifdef DEBUG			
	cout << "blockKey [" << blockKey << "]" << endl;
#endif
	map<string,llCommandList>::iterator blockIt = block.find(blockKey);
	if( block.end() != blockIt )
	{
		llCommandList blockCmd = blockIt->second;
		llCommandList::reverse_iterator cmdIt = blockCmd.rbegin();
		for( ; blockCmd.rend() != cmdIt ; cmdIt++ )
		{
#ifdef DEBUG			
			cout << "cmd [" << (*cmdIt)->m_Name << "]" << endl;
#endif
			commandsSrc.push_front(*cmdIt);
		}
	}
	else
	{
#ifdef DEBUG
		cout<<"if condition if not catch "<<endl;
#endif
	}
}
/****************************************************************************************************/
/****************************************************************************************************/
/****************************************************************************************************/
/****************************************************************************************************/
/****************************************************************************************************/
/****************************************************************************************************/
/****************************************************************************************************/
/****************************************************************************************************/
/****************************************************************************************************/
void llCompileCommand__include_directories::parseCompileCommand(void)
{
#ifdef DEBUG			
	cout<<"parseCompileCommand command ["<<m_Name<<"]"<<endl;
#endif
//	for( llTokenList::iterator itr = m_Tokens.begin(); m_Tokens.end() != itr ; itr++ )
//	{
//		cout<<" <"<<itr->m_Token<<":"<<itr->m_Joint<<"> ";
//	}
//	cout<<endl;
	m_Owner->extractTokensVars(m_Tokens);
//	for( llTokenList::iterator itr = m_Tokens.begin(); m_Tokens.end() != itr ; itr++ )
//	{
//		cout<<" <"<<itr->m_Token<<":"<<itr->m_Joint<<"> ";
//	}
	m_Owner->jointTokens(m_Tokens);
	for( llTokenList::iterator itr = m_Tokens.begin(); m_Tokens.end() != itr ; itr++ )
	{
//		cout<<" <"<<itr->m_Token<<":"<<itr->m_Joint<<"> ";
		string headerPath = m_Owner->getFolder() + "/" + itr->m_Token;
		headerPath = global_SystemInfo->simplePath(headerPath);
		string includeString("-I");
		includeString += headerPath;
#ifdef DEBUG			
		cout<<"includeString ["<<includeString<<"]"<<endl;
#endif
		string type = "cflag";
		m_Owner->insertFlags(type,includeString);
		type = "cxxflag";
		m_Owner->insertFlags(type,includeString);
	}

}
/****************************************************************************************************/
/****************************************************************************************************/
/****************************************************************************************************/
void llCompileCommand__add_definitions::parseCompileCommand(void)
{
#ifdef DEBUG			
	cout<<"parseCompileCommand command ["<<m_Name<<"]"<<endl;
#endif
	m_Owner->extractTokensVars(m_Tokens);
	m_Owner->jointTokens(m_Tokens);
	for( llTokenList::iterator itr = m_Tokens.begin(); m_Tokens.end() != itr ; itr++ )
	{
		string defines = itr->m_Token;
		string type = "cflag";
		m_Owner->insertFlags(type,defines);
		type = "cxxflag";
		m_Owner->insertFlags(type,defines);
	}
}
/****************************************************************************************************/
/****************************************************************************************************/
/****************************************************************************************************/
void llCompileCommand__add_library::parseCompileCommand(void)
{
#ifdef DEBUG			
	cout<<"parseCompileCommand command ["<<m_Name<<"]"<<endl;
#endif
	llTokenNode targetName = m_Tokens.front();
	m_Tokens.pop_front();
	llTargetNode target;
	target.m_Name = targetName.m_Token;
	
	while(false == m_Tokens.empty())
	{
		llTokenNode cur = m_Tokens.front();
		if ( "STATIC" == cur.m_Token )
		{
			target.m_Type.push_back("STATIC");
		}
		else if("SHARED" == cur.m_Token)
		{
			target.m_Type.push_back("SHARED");
		}
		else if("MODULE" == cur.m_Token)
		{
			target.m_Type.push_back("MODULE");
		}
		else if("|" == cur.m_Token)
		{
		}
		else
		{
			break;
		}
		m_Tokens.pop_front();
	}
// default 	STATIC
	if(target.m_Type.empty())
	{
		target.m_Type.push_back("STATIC");
	}

	m_Owner->extractTokensVars(m_Tokens);
	m_Owner->jointTokens(m_Tokens);

	string allObject = parseSource();
	llExecuteCommandNode wait = {"${WAIT}",""};
	m_Owner->insertExecuteCommand(wait);
	string linkString;
	llExecuteCommandNode bclink = {"${BC_LINK}",""};
	linkString += " -o ";
	linkString += global_SystemInfo->m_PWD + "/Bytecode/" + targetName.m_Token +".bc ";
	linkString += allObject;
#ifdef DEBUG			
	cout<<"linkString ["<<linkString<<"]"<<endl;
#endif
	bclink.m_Body = linkString;
	bclink.m_Src.push_back(allObject);
	bclink.m_Dst.push_back(global_SystemInfo->m_PWD + "/Bytecode/" + targetName.m_Token +".bc ");
	m_Owner->insertExecuteCommand(bclink);
	target.m_Path = global_SystemInfo->m_PWD + "/Bytecode/" + targetName.m_Token +".bc ";
	m_Owner->insertTarget(targetName.m_Token,target);
}
/****************************************************************************************************/
/****************************************************************************************************/
/****************************************************************************************************/
void llCompileCommand__add_executable::parseCompileCommand(void)
{
#ifdef DEBUG			
	cout<<"parseCompileCommand command ["<<m_Name<<"]"<<endl;
#endif
	llTokenNode targetName = m_Tokens.front();
	m_Tokens.pop_front();
	m_Owner->extractTokensVars(m_Tokens);
	m_Owner->jointTokens(m_Tokens);
	string allObject = parseSource();
	llExecuteCommandNode wait = {"${WAIT}",""};
	m_Owner->insertExecuteCommand(wait);
	string linkString;
	llExecuteCommandNode bclink = {"${BC_LINK}",""};
	linkString += " -o ";
	linkString += global_SystemInfo->m_PWD + "/Bytecode/" + targetName.m_Token +".bc ";
	linkString += allObject;
#ifdef DEBUG			
	cout<<"linkString ["<<linkString<<"]"<<endl;
#endif
	bclink.m_Body = linkString;
	bclink.m_Src.push_back(allObject);
	bclink.m_Dst.push_back(global_SystemInfo->m_PWD + "/Bytecode/" + targetName.m_Token +".bc ");
	m_Owner->insertExecuteCommand(bclink);
	llTargetNode target;
	target.m_Name = targetName.m_Token;
	target.m_Path = global_SystemInfo->m_PWD + "/Bytecode/" + targetName.m_Token +".bc ";
	target.m_Type.push_back("EXEC");
	m_Owner->insertTarget(targetName.m_Token,target);
}

/****************************************************************************************************/
/****************************************************************************************************/
/****************************************************************************************************/
void llCompileCommand__target_link_libraries::parseCompileCommand(void)
{
#ifdef DEBUG			
	cout<<"parseCompileCommand command ["<<m_Name<<"]"<<endl;
#endif
	llTokenNode targetName = m_Tokens.front();
	m_Tokens.pop_front();
	m_Owner->extractTokensVars(m_Tokens);
	m_Owner->jointTokens(m_Tokens);
	list<string> linkLibs;
	for( llTokenList::iterator itr = m_Tokens.begin(); m_Tokens.end() != itr ; itr++ )
	{
		linkLibs.push_back(itr->m_Token);
	}
	bool ret = m_Owner->appendTargetLink(targetName.m_Token,linkLibs);
	if( false == ret )
	{
		llReporter warn(" target_link_libraries have not correct name");
		warn.append(targetName);
		warn.Warning();
	}
}